﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace 线程池
{
    /// <summary>
    /// 自定义并发数量 线程池
    /// </summary>
    public class MyTask : IDisposable
    {
        private QueueScheduler TaskQueue { get; }

        /// <summary>
        /// 执行任务并发线程数
        /// </summary>
        public int Num { get; }

        /// <summary>
        ///
        /// </summary>
        /// <param name="num">并发线程数量</param>
        public MyTask(int num, bool wait = false)
        {
            Wait = wait;
            TaskQueue = new QueueScheduler(num);
            Num = num;
        }

        /// <summary>
        /// 释放后是否等待队列所有任务完成  <see langword="true"/>  等待队列任务全部完成 <see langword="false"/>  立即结束
        /// </summary>
        public bool Wait { get; }

        /// <summary>
        /// 执行任务
        /// </summary>
        /// <param name="action"></param>
        public void Run(Action action)
        {
            if (!GetState()) return;
            var T = new Task(action);//创建任务
            T.Start(TaskQueue);//启动任务
        }

        /// <summary>
        /// 执行任务
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func"></param>
        /// <returns></returns>
        public Task<T> RunAsync<T>(Func<T> func)
        {
            if (!GetState()) return null;
            var Ts = new Task<T>(func);//创建任务
            Ts.Start(TaskQueue);//启动任务
            return Ts;
        }

        /// <summary>
        /// 获取当前执行状态 <see langword="true"/> 正在执行中 <see langword="false"/>   已被释放
        /// </summary>
        /// <returns></returns>
        private bool GetState() => Interlocked.CompareExchange(ref State, 1, 1) == 0;

        /// <summary>
        /// 是否释放 0 未释放  1 已释放
        /// </summary>
        private int State;

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose() => Dispose(Wait);

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing">是否等待已投递任务执行完毕   <see langword="true"/> 为等待 <see langword="false"/> 为不等待</param>
        public void Dispose(bool disposing)
        {
            //判断状态为 0(初始状态) 后赋值 为 1(释放状态)
            if (Interlocked.CompareExchange(ref State, 1, 0) == 0) TaskQueue.Dispose(disposing);
        }

        private class QueueScheduler : TaskScheduler, IDisposable
        {


            /// <summary>
            /// 任务队列
            /// </summary>
            private BlockingCollection<Task> Queue { get; }

            /// <summary>
            /// 启动线程数量
            /// </summary>
            private readonly int StarTnum;

            /// <summary>
            /// 结束线程数量
            /// </summary>
            private int FinishTnum;

            /// <summary>
            ///
            /// </summary>
            /// <param name="num">启动线程数量</param>
            public QueueScheduler(int num)
            {
                Queue = new BlockingCollection<Task>();
                StarTnum = num;
                for (int i = 0; i < num; i++)
                {
                    Thread thread = new Thread(ThreadStart)
                    {
                        IsBackground = true//设为为后台线程，当主线程结束时线程自动结束
                    };
                    thread.Start();//启动后台线程检测等待执行投递任务
                }
            }

            /// <summary>
            /// 线程执行任务  等待队列投递执行任务
            /// </summary>
            private void ThreadStart()
            {
                try
                {
                    //这里还有点 问题
                    //获取队列任务  等待三十秒  或 是否释放为 0 未释放 满足一个条件继续执行
                    while (Queue.TryTake(out Task t, Timeout.Infinite))
                    {
                        if (!GetWait()) break;
                        //如有从队列取出任务  则执行任务
                        else if (t != null) TryExecuteTask(t);//在当前线程执行Task
                    }
                }
                catch (Exception ex)
                {
                    ;
                }
                finally
                {
                    //判断启动线程数量是否与 完成数量相等 并递增完成数量
                    if (StarTnum == Interlocked.Increment(ref FinishTnum))
                    {
                        Queue.Dispose();//释放队列
                        mre.Set();
                    }
                    else Queue.Add(null);//没有结束还有线程 压入null 是线程结束
                }
            }

            /// <summary>
            /// 返回队列枚举
            /// </summary>
            /// <returns></returns>
            protected override IEnumerable<Task> GetScheduledTasks()
            {
                return Queue.GetConsumingEnumerable();
            }

            /// <summary>
            /// 将Task加入到队列中
            /// </summary>
            /// <param name="task"></param>
            protected override void QueueTask(Task task)
            {
                //如果为 0 则赋值为 0  并返回原始值
                if (GetState()) Queue.Add(task);
            }

            /// <summary>
            /// 当执行该函数时，程序正在尝试以同步的方式执行Task代码
            /// </summary>
            /// <param name="task"></param>
            /// <param name="taskWasPreviouslyQueued"></param>
            /// <returns></returns>
            protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
            {
                return false;
            }

            /// <summary>
            /// 是否释放 0 未释放  1 已释放
            /// </summary>
            private int State;

            /// <summary>
            /// 释放后 0 为继续执行队列任务   1  为结束任务
            /// </summary>
            private int Wait;

            /// <summary>
            /// 获取当前状态 <see langword="true"/>  正常执行中 <see langword="false"/> 已被释放
            /// </summary>
            /// <returns></returns>
            private bool GetState() => Interlocked.CompareExchange(ref State, 0, 0) == 0;

            /// <summary>
            /// 判断 是否还需要执行任务
            /// </summary>
            /// <returns></returns>
            private bool GetWait()
                //判断是否呗释放  没有被释放 或 继续执行队列任务 都需要执行任务
                => GetState() ||
                //如果是等待执行完毕 剩余任务必须大于 0 因为有最后添加的null
                (Interlocked.CompareExchange(ref Wait, 0, 0) == 0 && Queue.Count > 0);

            /// <summary>
            /// 释放资源
            /// </summary>
            public void Dispose() => Dispose(false);


            private ManualResetEvent mre;
            /// <summary>
            ///释放资源
            /// </summary>
            /// <param name="disposing">是否等待已投递任务执行完毕  <see langword="true"/>为等待  <see langword="false"/>为不等待</param>
            public void Dispose(bool disposing)
            {
                //如果为 0 则赋值为 1  并返回原始值
                if (Interlocked.CompareExchange(ref State, 1, 0) == 0)
                {
                    mre = new ManualResetEvent(!disposing);
                    //如果不等待则赋值为 0  不等待赋值为1
                    int wait = disposing ? 0 : 1;
                    Interlocked.Exchange(ref Wait, wait);
                    Queue.Add(null);
                    mre.WaitOne();
                }
            }
        }
    }
}